import cv2
import os
import sys
import time
import threading
import subprocess
from datetime import datetime, timedelta
import configparser
import serial
import re
from loguru import logger

# 设置FFmpeg日志级别，屏蔽HEVC解码警告
os.environ['OPENCV_FFMPEG_LOGLEVEL'] = '-8'     # OpenCV FFmpeg静音
os.environ['FFMPEG_LOGLEVEL'] = 'quiet'         # FFmpeg静音

# 在程序开始时重定向stderr，保存原始stderr供loguru使用
import sys
import io
# 创建一个过滤器，只屏蔽特定的错误信息
class FilteredStderr:
    def __init__(self, original_stderr):
        self.original_stderr = original_stderr
        self.buffer = io.StringIO()
        
    def write(self, text):
        # 如果是HEVC/OpenCV相关错误，就丢弃
        if any(keyword in text for keyword in ['hevc', 'HEVC', 'OpenCV: FFMPEG', 'OpenH264', 'Could not find ref', 'cu_qp_delta']):
            return
        # 其他错误正常输出
        self.original_stderr.write(text)
        
    def flush(self):
        self.original_stderr.flush()

# 替换stderr
original_stderr = sys.stderr
sys.stderr = FilteredStderr(original_stderr)

# 路径配置（初始化，将在main函数中从config.ini读取）
LOG_PATH = None
VIDEO_CACHE_FOLDER = None  
VIDEO_SAVE_FOLDER = None


def get_config():
    logger.info('读取配置文件...')
    config = configparser.ConfigParser()
    try:
        config.read('config.ini', encoding='utf-8')
    except Exception as e:
        logger.debug(f'使用utf-8编码读取配置文件失败！error={e}')
        try:
           config.read('config.ini', encoding='utf-8-sig')
        except Exception as e:
            logger.debug(f'使用utf-8-sig编码读取配置文件失败！error={e}')
    config_dict = {}
    for section in config.sections():
        for option in config.options(section):
            config_dict[option] = config.get(section, option)
    return config_dict


def now_time():
    return time.strftime("%Y%m%d_%H%M%S", time.localtime())

class SerialReader:
    def __init__(self, port='COM4', baudrate=9600, timeout=1, debug_mode=False):
        """初始化串口连接
        :param debug_mode: 是否启用调试模式(模拟数据)
        """
        self.debug_mode = debug_mode
        self.running = True
        
        if self.debug_mode:
            # 调试模式配置
            self.ser = None
            self.mock_counter = 0
            self.mock_data_sequence = [
                0, 0, 0, 0, 0,  # 空载
                600, 650, 700, 750, 800,  # 上称
                750, 700, 650, 600,  # 称重中
                500, 400, 300, 200, 100, 0, 0, 0, 0, 0  # 下称
            ]
            self.last_data = [b'0000000']  # 初始空载
            logger.info("调试模式: 使用模拟称重数据")
        else:
            # 正常串口模式
            try:
                self.ser = serial.Serial(port, baudrate, timeout=timeout)
                logger.debug(f"串口 {port} 成功打开.")
                self.last_data = [b'0000000']  # 初始空载
            except serial.SerialException as e:
                logger.warning(f"无法打开串口 {port}: {e}")
                self.ser = None
                self.last_data = [b'0000000']  # 失败时也初始化数据

    def read_data(self):
        """读取串口数据"""
        while self.running:
            if self.debug_mode:
                # 模拟数据
                mock_value = self.mock_data_sequence[self.mock_counter % len(self.mock_data_sequence)]
                self.mock_counter += 1
                self.last_data = [f"{mock_value:07d}".encode('utf-8')]
                time.sleep(1)  # 模拟数据更新间隔
            elif self.ser is None:
                logger.warning("串口未成功打开，无法读取数据.")
                time.sleep(1)
                continue
            else:
                # 真实串口数据
                try:
                    if self.ser.in_waiting > 0:
                        line = self.ser.read(self.ser.in_waiting)
                        data_list = re.findall(rb'\x02(.*?)\x03', line)
                        if data_list:
                            self.last_data = list(sorted(set(data_list), key=data_list.index))
                    time.sleep(0.3)
                except Exception as e:
                    logger.error(f"读取串口数据时发生错误: {e}")
                    time.sleep(1)

    def get_latest_weight(self):
        """获取最新读取的重量"""
        try:
            weight_value = int(self.last_data[-1].decode('utf-8')[:7])
        except Exception as e:
            logger.warning(f'读取地磅读数失败: {e}')
            weight_value = 0
        return weight_value

    def close(self):
        """关闭串口"""
        self.running = False  # 停止读取线程
        if self.ser:
            self.ser.close()  # 关闭串口
            logger.info("串口已关闭.")

class WeightMonitor:
    def __init__(self, serial_reader):
        self.serial_reader = serial_reader
        self.current_weight_state = "空载"  # 当前称重状态
        self.actual_start_time = None  # 真实上称时间
        self.actual_end_time = None  # 真实下称完成时间
        self.video_files_to_merge = []  # 要拼接的视频文件

    def extract_weight(self):
        """从传感器数据中提取重量"""
        weight = self.serial_reader.get_latest_weight()
        return weight

    def monitor(self):
        while True:
            try:
                weight = self.extract_weight()
                if self.current_weight_state == "空载" and weight >= DetectThreshold:
                    self.actual_start_time = datetime.now()  # 记录真实上称时间
                    logger.info("🚛 检测到车辆上称！")
                    logger.info(f"   上称时间: {self.actual_start_time.strftime('%H:%M:%S')}")
                    logger.info(f"   当前重量: {weight}kg")
                    logger.info(f"   将录制完整过程（前推{Delay_Before_WEIGHT}秒 + 称重过程 + 延后{Delay_AFTER_WEIGHT}秒）")
                    self.current_weight_state = "称重中"

                elif self.current_weight_state == "称重中" and weight < DetectThreshold:
                    duration = (datetime.now() - self.actual_start_time).total_seconds()
                    logger.info(f"🚛 车辆开始下称 (称重时长: {duration:.1f}秒)")
                    logger.info(f"   当前重量: {weight}kg")
                    self.current_weight_state = "下称中"

                elif self.current_weight_state == "下称中" and weight == 0:
                    self.actual_end_time = datetime.now()  # 记录真实下称完成时间
                    total_duration = (self.actual_end_time - self.actual_start_time).total_seconds()
                    logger.info(f"✅ 车辆完全驶离地磅")
                    logger.info(f"   总称重时长: {total_duration:.1f}秒")
                    logger.info(f"   延后录制{Delay_AFTER_WEIGHT}秒后开始视频处理...")
                    self.current_weight_state = "空载"

                    # 延长 20 秒继续录制
                    time.sleep(Delay_AFTER_WEIGHT)
                    final_end_time = datetime.now()
                    
                    # 计算完整的录制时间范围：上称前20秒 到 下称后20秒
                    if self.actual_start_time is not None and self.actual_end_time is not None:
                        # 视频开始时间：上称时间前推20秒
                        video_start_time = self.actual_start_time - timedelta(seconds=Delay_Before_WEIGHT)
                        # 视频结束时间：下称完成后延后20秒
                        video_end_time = self.actual_end_time + timedelta(seconds=Delay_AFTER_WEIGHT)
                        
                        logger.info(f"📹 完整称重过程录制范围:")
                        logger.info(f"   开始: {video_start_time.strftime('%H:%M:%S')} (上称前{Delay_Before_WEIGHT}秒)")
                        logger.info(f"   上称: {self.actual_start_time.strftime('%H:%M:%S')}")
                        logger.info(f"   下称: {self.actual_end_time.strftime('%H:%M:%S')}")
                        logger.info(f"   结束: {video_end_time.strftime('%H:%M:%S')} (下称后{Delay_AFTER_WEIGHT}秒)")
                        
                        # 查找这个时间范围内的所有视频文件
                        search_start = video_start_time.timestamp()
                        search_end = video_end_time.timestamp()
                        
                        self.video_files_to_merge = find_videos_in_weighting(search_start, search_end)
                    # 拼接这段时间内的视频
                    merge_videos(self.video_files_to_merge, VIDEO_CACHE_FOLDER, VIDEO_SAVE_FOLDER)

                    # 清空临时文件列表
                    self.video_files_to_merge.clear()

                time.sleep(1)  # 轮询间隔
                
                # 显示当前状态 - 增加更多终端输出
                if self.current_weight_state == "称重中" and self.actual_start_time:
                    duration = (datetime.now() - self.actual_start_time).total_seconds()
                    logger.info(f"📊 正在称重... 重量: {weight}kg, 持续时间: {duration:.0f}秒")
                elif self.current_weight_state == "下称中":
                    logger.info(f"⏳ 等待车辆完全驶离... 当前重量: {weight}kg")
                elif self.current_weight_state == "空载":
                    # 增加空载状态的显示频率
                    if int(time.time()) % 10 == 0:  # 每10秒显示一次
                        cache_count = len([f for f in os.listdir(VIDEO_CACHE_FOLDER) if f.endswith('.mp4')])
                        logger.info(f"⌛ 系统待机中... 等待车辆上称 (阈值: {DetectThreshold}kg, 当前: {weight}kg, 缓存视频: {cache_count}个)")
                    # 轮询时，清除5分钟以前的视频缓存
                    clear_cache(clear_minutes_ago=5)
            except Exception as e:
                logger.error(f"监控过程中发生错误: {e}")

# ========== 线程1：持续录制视频 ==========

class VideoRecorder:
    def __init__(self):
        self.recording = True  # 用于控制视频录制的标志

    def video_recording_thread(self):
        try:
            logger.info("📹 启动视频录制线程...")
            
            # 使用默认后端，更好的兼容性
            cap = cv2.VideoCapture(rtsp_url)
            if not cap.isOpened():
                logger.error("❌ 无法打开摄像头视频流")
                return
            
            # 优化网络接收设置，减少传输错误
            cap.set(cv2.CAP_PROP_BUFFERSIZE, 2)  # 小缓冲但不为0，减少丢包
            cap.set(cv2.CAP_PROP_FPS, 25)  # 明确设置帧率
            # 增加超时时间，应对网络延迟
            cap.set(cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 10000)
            cap.set(cv2.CAP_PROP_READ_TIMEOUT_MSEC, 5000)
            # 尝试设置接收缓冲区（如果支持）
            try:
                cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('H','2','6','4'))
            except:
                pass
            
            logger.info("✅ 摄像头连接成功，开始持续录制")

            while self.recording:
                try:
                    start_time = time.time()
                    video_filename = f"{VIDEO_CACHE_FOLDER}/{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
                    # 使用最稳定的MP4V编码器，避免网络传输问题
                    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
                    fps = 25.0  # 匹配摄像头源帧率
                    out = cv2.VideoWriter(video_filename, fourcc, fps, (int(cap.get(3)), int(cap.get(4))))

                    frame_count = 0
                    while time.time() - start_time < TEMP_VIDEO_DURATION:
                        ret, frame = cap.read()
                        if not ret:
                            logger.debug("跳过无效帧")
                            time.sleep(0.04)  # 等待下一帧 (25fps = 40ms)
                            continue
                        
                        # 验证帧是否有效
                        if frame is None or frame.size == 0:
                            logger.debug("跳过空帧")
                            continue
                            
                        out.write(frame)
                        frame_count += 1

                    out.release()  # 保存视频段
                    logger.info(f"📹 保存视频片段: {os.path.basename(video_filename)} (共{frame_count}帧)")
                except Exception as e:
                    logger.error(f"视频录制时发生错误: {e}")
        except Exception as e:
            logger.error(f"摄像头初始化失败: {e}")
        finally:
            cap.release()


def find_videos_in_weighting(start_timestamp, end_timestamp):
    """查找称重时间段内的视频文件，排除正在录制的片段"""
    videos_in_weighting = []
    current_time = time.time()
    
    for filename in os.listdir(VIDEO_CACHE_FOLDER):
        if not filename.endswith('.mp4'):
            continue
            
        filepath = os.path.join(VIDEO_CACHE_FOLDER, filename)
        if not os.path.isfile(filepath):
            continue
            
        modify_time = os.path.getmtime(filepath)
        
        # 检查视频文件是否在称重时间段内
        # 1. 视频开始时间在称重时间段内，或
        # 2. 视频结束时间在称重时间段内，或  
        # 3. 视频时间段完全包含称重时间段
        video_start = modify_time
        video_end = modify_time + TEMP_VIDEO_DURATION
        
        # 不再预先排除文件，让merge_videos函数来判断文件是否可用
            
        # 检查时间重叠
        if (video_start <= end_timestamp and video_end >= start_timestamp):
            videos_in_weighting.append(filepath)
            logger.info(f"✅ 匹配视频: {filename}")
            logger.info(f"   创建时间: {datetime.fromtimestamp(modify_time).strftime('%H:%M:%S')}")
            logger.info(f"   视频时间: {datetime.fromtimestamp(video_start).strftime('%H:%M:%S')} - {datetime.fromtimestamp(video_end).strftime('%H:%M:%S')}")
        else:
            logger.debug(f"❌ 跳过视频: {filename} (时间不匹配)")
            logger.debug(f"   视频时间: {datetime.fromtimestamp(video_start).strftime('%H:%M:%S')} - {datetime.fromtimestamp(video_end).strftime('%H:%M:%S')}")
    
    # 按修改时间排序
    videos_in_weighting.sort(key=lambda x: os.path.getmtime(x))
    logger.info(f"📊 共找到 {len(videos_in_weighting)} 个视频文件需要合并")
    
    # 显示最终的时间范围
    if videos_in_weighting:
        first_video_time = os.path.getmtime(videos_in_weighting[0])
        last_video_time = os.path.getmtime(videos_in_weighting[-1]) + TEMP_VIDEO_DURATION
        logger.info(f"📊 实际视频覆盖时间: {datetime.fromtimestamp(first_video_time).strftime('%H:%M:%S')} - {datetime.fromtimestamp(last_video_time).strftime('%H:%M:%S')}")
        
    return videos_in_weighting

def merge_videos(video_files, input_path, output_path):
    """拼接视频文件，自动排除正在录制的文件
    :param video_files: 要拼接的视频文件列表
    :param input_path: 视频文件所在目录
    :param output_path: 输出目录
    """
    if not video_files:
        logger.warning("没有视频文件需要拼接")
        return
    
    # 读取ffmpeg路径配置
    config = get_config()
    ffmpeg_path = config.get('ffmpeg_path', 'ffmpeg').strip()
    
    # 如果配置为空，使用默认的ffmpeg
    if not ffmpeg_path:
        ffmpeg_path = 'ffmpeg'
    
    # 处理ffmpeg路径：支持相对路径和绝对路径
    if ffmpeg_path != 'ffmpeg':  # 如果不是默认的ffmpeg，需要处理路径
        # 如果是相对路径，则相对于exe或脚本所在目录
        if not os.path.isabs(ffmpeg_path):
            # 获取程序基准目录
            if getattr(sys, 'frozen', False):
                # 打包后的exe环境
                base_dir = os.path.dirname(sys.executable)
            else:
                # 开发环境
                base_dir = os.path.dirname(os.path.abspath(__file__))
            
            ffmpeg_path = os.path.abspath(os.path.join(base_dir, ffmpeg_path))
            logger.debug(f"将相对路径转换为绝对路径: {ffmpeg_path}")
        
        # 验证文件是否存在
        if not os.path.exists(ffmpeg_path):
            logger.error(f"❌ 找不到ffmpeg文件: {ffmpeg_path}")
            logger.info("请检查config.ini中的ffmpeg_path配置")
            logger.info("支持格式:")
            logger.info("  相对路径: ./ffmpeg.exe 或 ffmpeg-master/bin/ffmpeg.exe")
            logger.info("  绝对路径: C:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe")
            return
        else:
            logger.debug(f"✅ 使用配置的ffmpeg路径: {ffmpeg_path}")
    else:
        logger.debug("使用系统PATH中的ffmpeg")
    
    # 尝试合并，从最多文件开始，逐步减少
    files_to_try = video_files.copy()
    
    while files_to_try:
        try:
            result = _try_merge_videos(files_to_try, input_path, output_path, ffmpeg_path)
            if result:
                logger.info(f"✅ 成功合并 {len(files_to_try)} 个视频文件")
                return
        except Exception as e:
            logger.debug(f"合并 {len(files_to_try)} 个文件失败: {e}")
        
        # 移除最后一个文件（可能正在录制）再试
        removed_file = files_to_try.pop()
        logger.info(f"🔄 移除可能正在录制的文件重试: {os.path.basename(removed_file)}")
    
    logger.error("❌ 所有视频文件都无法合并")

def _try_merge_videos(video_files, input_path, output_path, ffmpeg_path):
    """尝试合并视频文件，返回True表示成功，False或抛出异常表示失败"""
    try:
        # 创建按年月组织的输出目录
        today = datetime.now().strftime("%Y%m")  # 格式: 2025/07
        output_dir = os.path.join(output_path, today)
        os.makedirs(output_dir, exist_ok=True)
        
        # 生成唯一临时文件名
        temp_id = datetime.now().strftime("%Y%m%d_%H%M%S")
        input_list = os.path.join(input_path, f"input_{temp_id}.txt")
        
        # 写入待拼接文件列表
        try:
            with open(input_list, "w", encoding='utf-8') as f:
                for video in video_files:
                    # 将路径转换为适合Windows的格式，并使用绝对路径
                    abs_path = os.path.abspath(video).replace('\\', '/')
                    f.write(f"file '{abs_path}'\n")
        except Exception as e:
            logger.error(f"创建输入文件列表失败: {e}")
            return

        # 生成输出文件名
        start_time = datetime.fromtimestamp(os.path.getmtime(video_files[0]))
        end_time = datetime.fromtimestamp(os.path.getmtime(video_files[-1]))
        final_name = f"record_{start_time.strftime('%Y%m%d_%H%M%S')}_to_{end_time.strftime('%H%M%S')}.mp4"
        final_path = os.path.join(output_dir, final_name)

        # 使用subprocess调用ffmpeg
        cmd = [
            ffmpeg_path,  # 使用配置文件中的ffmpeg路径
            "-loglevel", "quiet",  # 完全静音，只在发生致命错误时输出
            "-f", "concat",
            "-safe", "0",
            "-i", input_list,
            "-c", "copy",
            "-y",  # 覆盖已存在文件
            final_path
        ]
        
        logger.info("🎬 开始处理称重视频...")
        logger.info(f"   视频片段数量: {len(video_files)}个")
        logger.info(f"   时间范围: {start_time.strftime('%H:%M:%S')} - {end_time.strftime('%H:%M:%S')}")
        logger.info(f"   输出文件: {final_name}")
        logger.info("   正在拼接中...")
        
        try:
            result = subprocess.run(
                cmd,
                check=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                encoding='utf-8'
            )
            
            # 检查是否有HEVC解码警告
            if result.stderr and any(warning in result.stderr for warning in ['hevc', 'Could not find ref', 'cu_qp_delta']):
                logger.warning("⚠️  检测到HEVC解码警告，但视频拼接继续进行")
                logger.debug(f"HEVC警告详情: {result.stderr}")
            
            logger.info("✅ 视频拼接完成！")
            logger.info(f"   保存位置: {final_path}")
            
            # 显示文件大小
            try:
                file_size = os.path.getsize(final_path) / (1024*1024)  # MB
                logger.info(f"   文件大小: {file_size:.1f}MB")
            except:
                pass
        except subprocess.CalledProcessError as e:
            # 即使有HEVC错误，如果文件成功生成也认为是成功的
            if os.path.exists(final_path) and os.path.getsize(final_path) > 0:
                logger.warning("⚠️  视频拼接完成，但过程中有错误信息")
                logger.debug(f"错误详情: {e.stderr}")
                file_size = os.path.getsize(final_path) / (1024*1024)
                logger.info(f"   保存位置: {final_path}")
                logger.info(f"   文件大小: {file_size:.1f}MB")
                # 即使有警告，文件生成成功就算成功
            else:
                logger.error("❌ 视频拼接失败！")
                logger.error(f"   错误信息: {e.stderr}")
                return False
        finally:
            # 确保删除临时文件
            try:
                os.remove(input_list)
            except Exception as e:
                logger.warning(f"删除临时文件失败: {e}")

        # 清除已拼接的视频缓存
        clear_cache(specified_videos=video_files)
        return True  # 成功完成
        
    except Exception as e:
        logger.error(f"视频拼接过程中发生错误: {e}")
        return False


def clear_cache(remove_all=False, clear_minutes_ago=5, specified_videos=None):
    """
    清理缓存视频
    :param specified_videos:
    :param remove_all: 如果为True，清理全部缓存，一般在启动初始化时
    :param clear_minutes_ago: 配置几分钟前的视频被自动清空，默认5分钟
    :return:
    """
    if specified_videos is None:
        specified_videos = []
    current_time = time.time()
    five_minutes_ago = current_time - clear_minutes_ago * 60  # 5分钟前的时间戳
    try:
        cache_videos = os.listdir(VIDEO_CACHE_FOLDER)

        if remove_all:
            logger.info('🗑️  启动时清理全部缓存视频')
        elif specified_videos:
            # 清除指定视频列表的缓存，用在称重结束拼接完成时
            cache_videos = specified_videos
            logger.info(f'🗑️  清理已使用的视频缓存 ({len(cache_videos)}个文件)')
        else:
            # 清除5分钟前的视频，用在轮询监控时，清除以前的无用缓存
            cache_videos = [v for v in os.listdir(VIDEO_CACHE_FOLDER) if os.path.getmtime(os.path.join(VIDEO_CACHE_FOLDER, v)) < five_minutes_ago]
        # 开始清除
        for v in cache_videos:
            video_path = os.path.join(VIDEO_CACHE_FOLDER, v)
            if os.path.isfile(video_path):
                logger.debug(f'   删除: {os.path.basename(video_path)}')
                try:
                    os.remove(video_path)
                except Exception as e:
                    logger.warning(f'删除缓存视频“{video_path}”失败！error={e}')
    except Exception as e:
        logger.error(f"清理缓存视频过程中发生错误: {e}")

class ThreadMonitor:
    def __init__(self, threads):
        self.threads = threads
        self.thread_targets = {
            name: (thread._target, thread._args) for name, thread in threads.items()
        }

    def restart_thread(self, name, target, args):
        """重启指定的线程"""
        logger.warning(f"线程{name}已停止，正在重启...")
        new_thread = threading.Thread(target=target, args=args)
        new_thread.start()
        self.threads[name] = new_thread  # 更新字典中的线程

    def monitor_threads(self):
        """持续监控线程状态"""
        while True:
            for name, thread in self.threads.items():
                if not thread.is_alive():
                    # 从 thread_targets 中获取目标和参数来重启线程
                    target, args = self.thread_targets[name]
                    self.restart_thread(name, target, args)
            time.sleep(1)

def init():
    # 首先移除所有已存在的logger处理器
    logger.remove()
    # 文件日志：所有 INFO 及以上日志写入文件，自动轮转、压缩、保留
    logger.add(
        os.path.join(LOG_PATH, "Weight_monitor.log"),
        format="{time} {level} {message}",
        rotation="1 day",
        retention="90 days",
        compression="zip",
        level="DEBUG"
    )
    # 控制台日志：只打印 WARNING 及以上
    logger.add(
        sys.stderr,
        format="<green>{time}</green> <level>{level}</level> <level>{message}</level>",
        level="INFO"
    )
    logger.info('程序初始化...')
    clear_cache(remove_all=True)

def main():
    # 全局变量初始化
    global rtsp_url
    global TEMP_VIDEO_DURATION
    global Delay_AFTER_WEIGHT
    global Delay_Before_WEIGHT
    global DetectThreshold
    global port_com
    global LOG_PATH
    global VIDEO_CACHE_FOLDER
    global VIDEO_SAVE_FOLDER

    config = get_config()
    
    # 路径配置从config.ini读取，确保使用绝对路径
    # 获取exe所在目录或脚本所在目录作为基准路径
    if getattr(sys, 'frozen', False):
        # 打包后的exe环境
        base_dir = os.path.dirname(sys.executable)
    else:
        # 开发环境
        base_dir = os.path.dirname(os.path.abspath(__file__))
    
    LOG_PATH = os.path.abspath(os.path.join(base_dir, config.get('log_path', './log')))
    VIDEO_CACHE_FOLDER = os.path.abspath(os.path.join(base_dir, config.get('video_cache_folder', './videos/cache')))
    VIDEO_SAVE_FOLDER = os.path.abspath(os.path.join(base_dir, config.get('video_save_folder', './videos/录像')))
    
    # 确保所有目录存在
    os.makedirs(LOG_PATH, exist_ok=True)
    os.makedirs(VIDEO_CACHE_FOLDER, exist_ok=True)
    os.makedirs(VIDEO_SAVE_FOLDER, exist_ok=True)
    ####################################### 常量 ################################################
    # 摄像头配置
    username = config.get('username')
    password = config.get('password')
    ip_address = config.get('ip_address')
    rtsp_url = f'rtsp://{username}:{password}@{ip_address}:554/h264/ch1/main/av_stream'
    # rtsp_url = 0  # 本地摄像头（测试用）

    # 地磅串口
    port_com = config.get('com')

    # 配置常量
    TEMP_VIDEO_DURATION = float(config.get('temp_video_duration', 20))  # 每段视频的长度（秒）
    Delay_AFTER_WEIGHT = float(config.get('delay_after_weight', 20))  # 下称延时拍摄时间（秒）
    Delay_Before_WEIGHT = float(config.get('delay_before_weight', 20))  # 上称前推拍摄时间（秒）
    DetectThreshold = float(config.get('detect_threshold', 500))  # 检测阈值(Kg),只有>=检测阈值才会进行视频记录
    ############################################ 常量结束 #########################################
    # 确保视频保存目录存在
    os.makedirs(VIDEO_CACHE_FOLDER, exist_ok=True)
    # 初始化
    init()
    
    # 获取并显示ffmpeg路径配置
    ffmpeg_path_config = config.get('ffmpeg_path', 'ffmpeg').strip()
    if not ffmpeg_path_config:
        ffmpeg_display = "系统PATH中的ffmpeg"
    elif not os.path.isabs(ffmpeg_path_config):
        # 相对路径，转换为绝对路径显示
        ffmpeg_abs_path = os.path.abspath(os.path.join(base_dir, ffmpeg_path_config))
        ffmpeg_display = f"{ffmpeg_path_config} → {ffmpeg_abs_path}"
    else:
        # 绝对路径直接显示
        ffmpeg_display = ffmpeg_path_config
    
    # 显示系统配置信息
    logger.info("=" * 60)
    logger.info("地磅视频录制系统启动")
    logger.info("=" * 60)
    logger.info(f"摄像头地址: {rtsp_url}")
    logger.info(f"串口配置: COM{port_com}")
    logger.info(f"检测阈值: {DetectThreshold}kg")
    logger.info(f"视频片段长度: {TEMP_VIDEO_DURATION}秒")
    logger.info(f"前推时间: {Delay_Before_WEIGHT}秒, 延后时间: {Delay_AFTER_WEIGHT}秒")
    logger.info(f"FFmpeg路径: {ffmpeg_display}")
    logger.info(f"基准目录: {base_dir}")
    logger.info(f"日志路径: {LOG_PATH}")
    logger.info(f"视频缓存路径: {VIDEO_CACHE_FOLDER}")
    logger.info(f"视频保存路径: {VIDEO_SAVE_FOLDER}")
    logger.info(f"缓存目录是否存在: {os.path.exists(VIDEO_CACHE_FOLDER)}")
    logger.info("=" * 60)

    # 初始化线程
    serial_reader = SerialReader(port=f'COM{port_com}', debug_mode=False)  # 生产模式
    video_recorder = VideoRecorder()
    weight_monitor = WeightMonitor(serial_reader)

    serial_thread = threading.Thread(target=serial_reader.read_data)
    video_thread = threading.Thread(target=video_recorder.video_recording_thread)
    weight_thread = threading.Thread(target=weight_monitor.monitor)

    # 启动线程
    serial_thread.start()
    video_thread.start()
    weight_thread.start()

    # 初始化并启动监控器
    threads = {
        'serial_thread': serial_thread,
        'video_thread': video_thread,
        'weight_thread': weight_thread
    }
    thread_monitor = ThreadMonitor(threads)
    monitor_thread = threading.Thread(target=thread_monitor.monitor_threads)
    monitor_thread.start()

    # 等待线程结束（如果需要）
    serial_thread.join()
    video_thread.join()
    weight_thread.join()


# ========== 主程序入口 ==========

if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        logger.error(e)
        os.system('pause')